#!/usr/bin/env python3
"""
S3 Object Copy Tool - CopyObject API 演示工具

这个工具演示如何使用 CopyObject API 复制 S3 存储桶中的对象。
支持同桶和跨桶复制，以及元数据管理。

使用示例:
   python s3_object_copy.py --src-bucket my-bucket --src-key source.txt --dst-bucket my-bucket --dst-key dest.txt
   python s3_object_copy.py --src-bucket bucket1 --src-key file.txt --dst-bucket bucket2 --dst-key file.txt
   python s3_object_copy.py --src-bucket my-bucket --src-key source.txt --dst-bucket my-bucket --dst-key dest.txt --metadata-directive REPLACE --content-type text/plain
   python s3_object_copy.py --format json
   python s3_object_copy.py --verbose
"""

import argparse
import boto3
import json
import sys
from datetime import datetime, timezone
from botocore.config import Config
from botocore.exceptions import ClientError, NoCredentialsError


class ObjectCopyClient:
    """S3 对象复制客户端"""
    
    def __init__(self, endpoint_url='http://localhost:8000', 
                 access_key='AKIAIOSFODNN7EXAMPLE', 
                 secret_key='wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
                 region='us-east-1'):
        """
        初始化对象复制客户端
        
        Args:
            endpoint_url: S3 服务端点
            access_key: AWS Access Key
            secret_key: AWS Secret Key
            region: AWS 区域
        """
        self.endpoint_url = endpoint_url
        self.access_key = access_key
        self.secret_key = secret_key
        self.region = region
        self._client = None
    
    def _get_client(self):
        """获取 S3 客户端"""
        if self._client is None:
            self._client = boto3.client(
                's3',
                endpoint_url=self.endpoint_url,
                aws_access_key_id=self.access_key,
                aws_secret_access_key=self.secret_key,
                region_name=self.region,
        config=Config(s3={'addressing_style': 'path'})
    )
        return self._client
    
    def copy_object(self, src_bucket, src_key, dst_bucket, dst_key, 
                           metadata_directive='COPY', content_type=None, metadata=None):
        """简单格式复制对象"""
        try:
            s3_client = self._get_client()
            
            # 构建复制源
            copy_source = {
                'Bucket': src_bucket,
                'Key': src_key
            }
            
            # 准备参数
            copy_args = {
                'CopySource': copy_source,
                'Bucket': dst_bucket,
                'Key': dst_key,
                'MetadataDirective': metadata_directive
            }
            
            # 如果是 REPLACE 模式，设置新的元数据
            if metadata_directive == 'REPLACE':
                if content_type:
                    copy_args['ContentType'] = content_type
                if metadata:
                    copy_args['Metadata'] = metadata
            
            print(f"📋 复制对象:")
            print(f"   📤 源: s3://{src_bucket}/{src_key}")
            print(f"   📥 目标: s3://{dst_bucket}/{dst_key}")
            print(f"   🔧 元数据指令: {metadata_directive}")
            
            if metadata_directive == 'REPLACE':
                if content_type:
                    print(f"   📄 新 Content-Type: {content_type}")
                if metadata:
                    print(f"   🏷️  新自定义元数据: {metadata}")
            
            response = s3_client.copy_object(**copy_args)
            
            print(f"✅ 复制成功！")
            copy_result = response.get('CopyObjectResult', {})
            if copy_result:
                print(f"   🏷️  ETag: {copy_result.get('ETag')}")
                print(f"   📅 LastModified: {copy_result.get('LastModified')}")
            
            return True
            
        except ClientError as e:
            error_code = e.response.get('Error', {}).get('Code')
            error_message = e.response.get('Error', {}).get('Message')
            
            if error_code == 'NoSuchKey':
                print(f"❌ 源对象不存在: s3://{src_bucket}/{src_key}")
            elif error_code == 'NoSuchBucket':
                print(f"❌ 桶不存在")
            else:
                print(f"❌ 复制失败 [{error_code}]: {error_message}")
            return False
        except NoCredentialsError:
            print("❌ 错误: 未找到 AWS 凭据")
            return False
        except Exception as e:
            print(f"❌ 未知错误: {e}")
            return False
    
    def copy_object_verbose(self, src_bucket, src_key, dst_bucket, dst_key, 
                            metadata_directive='COPY', content_type=None, metadata=None):
        """详细格式复制对象"""
        try:
            s3_client = self._get_client()
            
            print("📋 对象复制详细信息:")
            print("=" * 60)
            print(f"   📤 源桶名称: {src_bucket}")
            print(f"   🔑 源对象键名: {src_key}")
            print(f"   📥 目标桶名称: {dst_bucket}")
            print(f"   🔑 目标对象键名: {dst_key}")
            print(f"   🌐 区域: {self.region}")
            print(f"   🔗 服务端点: {self.endpoint_url}")
            print(f"   📅 操作时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
            print(f"   🔧 元数据指令: {metadata_directive}")
            
            if metadata_directive == 'REPLACE':
                if content_type:
                    print(f"   📄 新 Content-Type: {content_type}")
                if metadata:
                    print(f"   🏷️  新自定义元数据: {metadata}")
            
            print()
            
            # 构建复制源
            copy_source = {
                'Bucket': src_bucket,
                'Key': src_key
            }
            
            # 准备参数
            copy_args = {
                'CopySource': copy_source,
                'Bucket': dst_bucket,
                'Key': dst_key,
                'MetadataDirective': metadata_directive
            }
            
            # 如果是 REPLACE 模式，设置新的元数据
            if metadata_directive == 'REPLACE':
                if content_type:
                    copy_args['ContentType'] = content_type
                if metadata:
                    copy_args['Metadata'] = metadata
            
            print(f"   🔧 正在复制对象...")
            response = s3_client.copy_object(**copy_args)
            
            print(f"   ✅ 复制成功！")
            copy_result = response.get('CopyObjectResult', {})
            if copy_result:
                print(f"      🏷️  ETag: {copy_result.get('ETag')}")
                print(f"      📅 LastModified: {copy_result.get('LastModified')}")
            
            print(f"   📊 操作完成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
            
            return True
            
        except ClientError as e:
            error_code = e.response.get('Error', {}).get('Code')
            error_message = e.response.get('Error', {}).get('Message')
            
            print(f"   ❌ 复制失败:")
            print(f"      错误代码: {error_code}")
            print(f"      错误信息: {error_message}")
            
            if error_code == 'NoSuchKey':
                print(f"      ❌ 源对象不存在: s3://{src_bucket}/{src_key}")
            elif error_code == 'NoSuchBucket':
                print(f"      ❌ 桶不存在")
            
            return False
        except NoCredentialsError:
            print("   ❌ 错误: 未找到 AWS 凭据")
            return False
        except Exception as e:
            print(f"   ❌ 未知错误: {e}")
            return False
    
    def copy_object_json(self, src_bucket, src_key, dst_bucket, dst_key, 
                         metadata_directive='COPY', content_type=None, metadata=None):
        """JSON 格式复制对象"""
        try:
            s3_client = self._get_client()
            
            # 构建复制源
            copy_source = {
                'Bucket': src_bucket,
                'Key': src_key
            }
            
            # 准备参数
            copy_args = {
                'CopySource': copy_source,
                'Bucket': dst_bucket,
                'Key': dst_key,
                'MetadataDirective': metadata_directive
            }
            
            # 如果是 REPLACE 模式，设置新的元数据
            if metadata_directive == 'REPLACE':
                if content_type:
                    copy_args['ContentType'] = content_type
                if metadata:
                    copy_args['Metadata'] = metadata
            
            response = s3_client.copy_object(**copy_args)
            
            copy_result = response.get('CopyObjectResult', {})
            
            result = {
                "success": True,
                "source_bucket": src_bucket,
                "source_key": src_key,
                "destination_bucket": dst_bucket,
                "destination_key": dst_key,
                "metadata_directive": metadata_directive,
                "etag": copy_result.get('ETag'),
                "last_modified": copy_result.get('LastModified').isoformat() if copy_result.get('LastModified') else None,
                "operation_time": datetime.now().isoformat()
            }
            
            if metadata_directive == 'REPLACE':
                if content_type:
                    result["new_content_type"] = content_type
                if metadata:
                    result["new_metadata"] = metadata
            
            print(json.dumps(result, indent=2, ensure_ascii=False))
            return True
            
        except ClientError as e:
            error_code = e.response.get('Error', {}).get('Code')
            error_message = e.response.get('Error', {}).get('Message')
            
            result = {
                "success": False,
                "source_bucket": src_bucket,
                "source_key": src_key,
                "destination_bucket": dst_bucket,
                "destination_key": dst_key,
                "error": error_code,
                "error_message": error_message,
                "operation_time": datetime.now().isoformat()
            }
            
            print(json.dumps(result, indent=2, ensure_ascii=False))
            return False
        except NoCredentialsError:
            result = {
                "success": False,
                "error": "NoCredentials",
                "error_message": "未找到 AWS 凭据",
                "operation_time": datetime.now().isoformat()
            }
            print(json.dumps(result, indent=2, ensure_ascii=False))
            return False
        except Exception as e:
            result = {
                "success": False,
                "error": "Unknown",
                "error_message": str(e),
                "operation_time": datetime.now().isoformat()
            }
            print(json.dumps(result, indent=2, ensure_ascii=False))
            return False


def parse_args():
    """解析命令行参数"""
    parser = argparse.ArgumentParser(
        description='S3 CopyObject API 演示工具',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
示例:
  %(prog)s --src-bucket my-bucket --src-key source.txt --dst-bucket my-bucket --dst-key dest.txt  # 同桶复制
  %(prog)s --src-bucket bucket1 --src-key file.txt --dst-bucket bucket2 --dst-key file.txt        # 跨桶复制
  %(prog)s --src-bucket my-bucket --src-key source.txt --dst-bucket my-bucket --dst-key dest.txt --metadata-directive REPLACE --content-type text/plain  # 替换元数据
  %(prog)s --format json                                                                          # JSON 格式输出
  %(prog)s --verbose                                                                              # 详细格式输出
  %(prog)s --access-key KEY --secret-key SECRET                                                   # 指定凭据
        """
    )
    
    parser.add_argument(
        '--host',
        default='http://localhost:8000',
        help='S3 服务端点 (默认: http://localhost:8000)'
    )
    
    parser.add_argument(
        '--access-key',
        default='AKIAIOSFODNN7EXAMPLE',
        help='AWS Access Key (默认: 测试密钥)'
    )
    
    parser.add_argument(
        '--secret-key',
        default='wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
        help='AWS Secret Key (默认: 测试密钥)'
    )
    
    parser.add_argument(
        '--region',
        default='us-east-1',
        help='AWS 区域 (默认: us-east-1)'
    )
    
    parser.add_argument(
        '--format',
        choices=['simple', 'verbose', 'json'],
        default='simple',
        help='输出格式 (默认: simple)'
    )
    
    parser.add_argument(
        '--verbose',
        action='store_true',
        help='详细输出模式 (等同于 --format verbose)'
    )
    
    parser.add_argument(
        '--src-bucket',
        required=True,
        help='源桶名称'
    )
    
    parser.add_argument(
        '--src-key',
        required=True,
        help='源对象键名'
    )
    
    parser.add_argument(
        '--dst-bucket',
        required=True,
        help='目标桶名称'
    )
    
    parser.add_argument(
        '--dst-key',
        required=True,
        help='目标对象键名'
    )
    
    parser.add_argument(
        '--metadata-directive',
        choices=['COPY', 'REPLACE'],
        default='COPY',
        help='元数据指令 (默认: COPY)'
    )
    
    parser.add_argument(
        '--content-type',
        help='新的 Content-Type（当 metadata-directive=REPLACE 时）'
    )
    
    parser.add_argument(
        '--metadata',
        action='append',
        help='新的自定义元数据 key=value（当 metadata-directive=REPLACE 时）'
    )
    
    return parser.parse_args()


def main():
    """主函数"""
    args = parse_args()
    
    # 如果指定了 --verbose，覆盖 format 设置
    if args.verbose:
        args.format = 'verbose'
    
    # 准备元数据
    metadata = None
    if args.metadata:
        metadata = {}
        for meta in args.metadata:
            if '=' in meta:
                key, value = meta.split('=', 1)
                metadata[key] = value
            else:
                print(f"⚠️  警告: 忽略无效元数据格式: {meta}")
    
    # 创建客户端
    client = ObjectCopyClient(
        endpoint_url=args.host,
        access_key=args.access_key,
        secret_key=args.secret_key,
        region=args.region
    )
    
    # 根据格式执行相应操作
    success = False
    if args.format == 'simple':
        success = client.copy_object_simple(
            args.src_bucket, args.src_key, args.dst_bucket, args.dst_key,
            args.metadata_directive, args.content_type, metadata
        )
    elif args.format == 'verbose':
        success = client.copy_object_verbose(
            args.src_bucket, args.src_key, args.dst_bucket, args.dst_key,
            args.metadata_directive, args.content_type, metadata
        )
    elif args.format == 'json':
        success = client.copy_object_json(
            args.src_bucket, args.src_key, args.dst_bucket, args.dst_key,
            args.metadata_directive, args.content_type, metadata
        )
    
    # 设置退出码
    sys.exit(0 if success else 1)


if __name__ == '__main__':
    main()